/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.ArrayList;
import java.util.List;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.patterns.AddRelationOTM;
import ro.dta.idbi.ui.sql.SqlGraph;
import uk.ac.ic.doc.automed.reps.Schema;

import com.mxgraph.model.mxCell;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;

/**
 * UI for the <i>addition of one-to-many relationship pattern</i>
 * 
 * @author Tudor Dobrila
 * 
 */
public class AddRelationOTMDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = 3374232653959036931L;

	/**
	 * Source table
	 */
	private ISqlTable sourceTbl;

	/**
	 * Target table
	 */
	private ISqlTable targetTbl;

	/**
	 * List of columns from the source table
	 */
	private List<ISqlColumn> sourceCols;

	/**
	 * List of columns from the target table
	 */
	private List<ISqlColumn> targetCols;

	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		if (objects.length != 2) {
			return false;
		}

		if (!(objects[0] instanceof ISqlTable) || !(objects[1] instanceof ISqlTable)) {
			return false;
		}

		return true;
	}

	/**
	 * Addition of one-to-many relationship pattern dialog class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public AddRelationOTMDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] element, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given elements");
		}

		sourceCols = new ArrayList<ISqlColumn>();
		targetCols = new ArrayList<ISqlColumn>();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.ui.patterns.AbstractTransDialog#createOriginalDiagram()
	 */
	@Override
	protected mxGraphComponent createOriginalDiagram() {
		final SqlGraph graph = new SqlGraph();
		AddSubsetDialog subDlg = new AddSubsetDialog(parentFrame, schema, element);
		subDlg.addOriginalTables(graph);
		return new mxGraphComponent(graph);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.AbstractTransDialog#createTransformedDiagram()
	 */
	@Override
	protected mxGraphComponent createTransformedDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.setCellsMovable(false);
		AddSubsetDialog subDlg = new AddSubsetDialog(parentFrame, schema, element);
		subDlg.addOriginalTables(graph);
		graph.addListener(mxEvent.CELL_CONNECTED, new mxIEventListener() {

			@Override
			public void invoke(final Object arg0, final mxEventObject arg1) {
				final mxCell edge = (mxCell) arg1.getProperty("edge");
				if (edge.getSource() != null && edge.getTarget() != null) {
					if (!(edge.getSource() instanceof ISqlColumn)
							|| !(edge.getTarget() instanceof ISqlColumn)
							|| (sourceTbl != null && edge.getSource() != sourceTbl)
							|| (targetTbl != null && edge.getTarget() != targetTbl)
							|| !(((ISqlColumn) edge.getTarget())).isPK()
							|| sourceCols.contains(edge.getSource())
							|| targetCols.contains(edge.getTarget())) {
						graph.getModel().beginUpdate();
						graph.getModel().remove(edge);
						graph.getModel().endUpdate();
					} else {
						if (sourceTbl == null) {
							sourceTbl = (ISqlTable) edge.getSource().getParent();
						}

						if (targetTbl == null) {
							targetTbl = (ISqlTable) edge.getTarget().getParent();
						}

						sourceCols.add((ISqlColumn) edge.getSource());
						targetCols.add((ISqlColumn) edge.getTarget());

						updateMacro();
					}
				}
			}
		});
		final mxGraphComponent gComp = new mxGraphComponent(graph);
		gComp.setConnectable(true);
		return gComp;
	}

	/**
	 * Update the macro command
	 */
	private void updateMacro() {
		StringBuilder cmd = new StringBuilder();
		cmd.append(AddRelationOTM.COMMAND).append(" (");
		cmd.append(sourceTbl.getValue()).append("_").append(targetTbl.getValue()).append("_fk, ");
		for (ISqlColumn c : sourceCols) {
			cmd.append("<<").append(sourceTbl.getValue()).append(",");
			cmd.append(c.getValue()).append(">>, ");
		}
		for (ISqlColumn c : targetCols) {
			cmd.append("<<").append(targetTbl.getValue()).append(",");
			cmd.append(c.getValue()).append(">>, ");
		}
		cmd.setLength(cmd.length() - 2);
		cmd.append(")");

		macroField.setText(cmd.toString());
	}

}
